#ifdef __linux__

#ifndef HASIO_DETAIL_EPOLL_REACTOR_HPP
#define HASIO_DETAIL_EPOLL_REACTOR_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include <sys/epoll.h>
#include "op_queue.hpp"
//#include "operation.hpp"
#include "reactor_op.hpp"
#include "unistd.h"
#include <mutex>

namespace hasio {
namespace detail {

class epoll_reactor
{
public:
    enum op_type
    {
        read_op = 0,
        write_op = 1,
        connect_op = 2,
        max_ops = 3,
    };

    using descriptor_t = int;

    class descriptor_state
        : private operation
    {
        friend class epoll_reactor;
    private:
        descriptor_t descriptor_;
        uint32_t registered_events_;
        op_queue<reactor_op> op_que_[max_ops];
        std::mutex mutex_;

        descriptor_state()
            : operation(&epoll_reactor::descriptor_state::do_complete)
            , descriptor_(-1)
            , registered_events_(0u)
        {}

        void set_ready_events(uint32_t events) { result_ = events; }

        void add_ready_events(uint32_t events) { result_ |= events; }

        operation* perform_io(uint32_t events)
        {
            static const int flag[max_ops] = { EPOLLIN, EPOLLOUT, EPOLLPRI };
            for (int i = max_ops - 1; i >= 0; --i)
            {
                if (events & (flag[i] | EPOLLERR | EPOLLHUP))
                {
                    while (reactor_op* op = op_que_[i].front())
                    {
                        if (reactor_op::status status = op->perform())
                        {
                            op_que_[i].pop();
                        }
                    }
                }
            }
            operation* op = op_que_[0].front();
            op_que_[0].pop();
            return op;
        }

        static void do_complete(operation *base)
        {
            descriptor_state* descriptor_data = static_cast<descriptor_state*>(base);
            //
            if (operation* op = descriptor_data->perform_io(0))
            {
                op->complete();
            }
        }

    };

    using descriptor_data_t = descriptor_state*;

public:
    epoll_reactor()
        : epoll_fd_(do_epoll_create())
    {
    }

    ~epoll_reactor()
    {
        if (epoll_fd_ != -1)
        {
            ::close(epoll_fd_);
            epoll_fd_ = -1;
        }
   }

    int register_descriptor(descriptor_t descriptor)
    {
        descriptor_state* descriptor_data = new descriptor_state();

        epoll_event ev = { 0, { 0 } };
        ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLPRI | EPOLLET;

        descriptor_data->descriptor_ = descriptor;
        descriptor_data->registered_events_ = ev.events;
        ev.data.ptr = descriptor_data;

        int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev);
        if (result != 0)
            return errno;

        return 0;
    }

    void deregister_descriptor(descriptor_t descriptor, 
        epoll_reactor::descriptor_data_t &descriptor_data)
    {
        if (descriptor_data->registered_events_ != 0)
        {
            epoll_event ev = {0, {0}};
            epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev);

            op_queue<operation> ops;
            for (int i = 0; i < max_ops; ++i)
            {
                ops.push(descriptor_data->op_que_[i]);
            }

            descriptor_data->descriptor_ = -1;

            // ops
        }
   }

    void run(op_queue<operation> &ops)
    {
        epoll_event events[128];
        int num_events = epoll_wait(epoll_fd_, events, 128, 5000);

        for (int i = 0; i < num_events; ++i)
        {
            void* ptr = events[i].data.ptr;
            descriptor_data_t descriptor_data = static_cast<descriptor_data_t>(ptr);
            descriptor_data->set_ready_events(events[i].events);
            ops.push(descriptor_data);
        }
    }

private:
    int do_epoll_create()
    {
        int fd = epoll_create(20000);

        if (fd == -1)
        {
            //
        }

        return fd;
    }
private:
    int epoll_fd_;
};

}
}

#endif // HASIO_DETAIL_EPOLL_REACTOR_HPP

#endif